package top.chendawei.main.facade;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.AntPathMatcher;
import top.chendawei.aop.auth.CustomsSecurityProperties;
import top.chendawei.aop.exceptions.CommonPartsExceptions;
import top.chendawei.aop.localcache.GuavaLocalCache;
import top.chendawei.system.beans.entity.SysAuth;
import top.chendawei.system.beans.entity.SysRole;
import top.chendawei.system.beans.entity.SysUser;
import top.chendawei.system.beans.vo.param.RoleAddParam;
import top.chendawei.system.service.IBlacklistService;
import top.chendawei.system.service.ICommonService;
import top.chendawei.system.service.impl.AuthServiceImpl;
import top.chendawei.util.fastjson.FastJsonUtil;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

@Slf4j
@Service
public class UserFacadeImpl {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private IBlacklistService blacklistService;

    @Autowired
    private CustomsSecurityProperties customsSecurityProperties;

    @Autowired
    private ICommonService commonService;

    @Autowired
    private AuthServiceImpl authService;
    public static final String CUR_USER = "Authorization";


    public String getToken(SysUser user) {
        String uid = UUID.randomUUID().toString();
        user.setToken(uid);
        user.setPassword(null);
        user.setT(System.currentTimeMillis());
        String token = JWT.create().withAudience(FastJsonUtil.toJSONString(user))
                .sign(Algorithm.HMAC256(uid));
        return token;
    }

    public String getDecode(String token) {
        try {
            String s = JWT.decode(token).getAudience().get(0);
            return s;
        } catch (Exception e) {
            log.error("getCookieUser error 3");
            return null;
        }
    }

    public SysUser getUserFromToken(HttpServletRequest request) {
        String token = request.getHeader(CUR_USER);
        if (StringUtils.isBlank(token)) {
            log.info("getCookieUser null 1");
            return null;
        }
        String userStr = getDecode(token);
        if (StringUtils.isBlank(userStr)) {
            log.info("getCookieUser error 2");
            return null;
        }
        SysUser u = FastJsonUtil.parse(userStr, SysUser.class);
        if (null == u) {
            return null;
        }
        Long t1 = System.currentTimeMillis() - u.getT();
        if (t1 > customsSecurityProperties.getSessionOutTime()) {
            throw new CommonPartsExceptions("Token过期，请重新登陆");
        }
        String md5Token = u.getToken();
        boolean contain = blacklistService.checkContain(md5Token);
        if (contain) {
            throw new CommonPartsExceptions("Token失效,在黑名单中，请重新登陆");
        }
        return u;
    }

    public List<Map<String, Object>> queryRouteListByUser(SysUser user) {
        //0平台对应的所有路由资源信息，整理成树形结果（localcache全量缓存，菜单发生变化时候应该重启）
        List<SysAuth> sas = authService.queryAllAuthRoteList(0);
        if (CollectionUtils.isEmpty(sas)) {
            return Collections.EMPTY_LIST;
        }
        boolean isAdmin = false;
        Set<Long> authIds = new HashSet<>();
        if (null != user && "admin11".equals(user.getUsername())) {
            isAdmin = true;
        } else {
            //根据角色Id查询其对应的资源
            authIds = authService.queryAuthIdList(user.getRoleIdList());
        }
        List<Map<String, Object>> r = Lists.newArrayListWithCapacity(sas.size());
        for (SysAuth auth : sas) {
            if (StringUtils.isNoneBlank(auth.getRouter()) && (isAdmin || authIds.contains(auth.getId()))) {
                Map<String, Object> m1 = Maps.newHashMap();
                m1.put("router", auth.getRouter());
                m1.put("name", auth.getName());
                int i[] = {0};
                collectRouter(m1, auth.getSubAuth(), isAdmin, authIds, i);
                r.add(m1);
            }
        }
        return r;
    }

    private void collectRouter(Map<String, Object> m1, List<SysAuth> subAuth, boolean isAdmin, Set<Long> authIds, int[] i) {
        if (CollectionUtils.isNotEmpty(subAuth) && i[0] < 10) {
            List<Map<String, Object>> children = Lists.newArrayListWithCapacity(subAuth.size());
            for (SysAuth l2 : subAuth) {
                if (StringUtils.isNoneBlank(l2.getRouter()) && (isAdmin || authIds.contains(l2.getId()))) {
                    Map<String, Object> m2 = Maps.newHashMap();
                    m2.put("router", l2.getRouter());
                    m2.put("name", l2.getName());
                    collectRouter(m2, l2.getSubAuth(), isAdmin, authIds, i);
                    children.add(m2);
                }
            }
            if (children.size() > 0) {
                m1.put("children", children);
            }
        }
    }


    public List<Map<String, Object>> queryAuthTreeByRoleId(Set<Long> expandedKeys) {
        List<SysAuth> sas = authService.queryAllAuthList(0);
        if (CollectionUtils.isEmpty(sas)) {
            return Collections.EMPTY_LIST;
        }
        List<Map<String, Object>> r = Lists.newArrayListWithCapacity(sas.size());
        for (SysAuth auth : sas) {
            if (StringUtils.isNoneBlank(auth.getRouter())) {
                Map<String, Object> m1 = Maps.newHashMap();
                m1.put("title", auth.getName() + "[" + auth.getId() + "]");
                m1.put("key", auth.getId());
                if (null != expandedKeys) {
                    expandedKeys.add(auth.getId());
                }
                int i[] = {0};
                collectSubAuth(m1, auth.getSubAuth(), i);
                r.add(m1);
            }
        }
        return r;
    }

    private void collectSubAuth(Map<String, Object> m1, List<SysAuth> subAuth, int[] i) {
        if (CollectionUtils.isNotEmpty(subAuth) && i[0] < 10) {
            i[0]++;
            List<Map<String, Object>> children = Lists.newArrayListWithCapacity(subAuth.size());
            for (SysAuth l2 : subAuth) {
                if (StringUtils.isNoneBlank(l2.getRouter())) {
                    Map<String, Object> m2 = Maps.newHashMap();
                    m2.put("key", l2.getId());
                    m2.put("title", l2.getName() + "[" + l2.getId() + "]");
                    if (CollectionUtils.isNotEmpty(l2.getSubAuth())) {
                        collectSubAuth(m2, l2.getSubAuth(), i);
                    }
                    children.add(m2);
                }
            }
            if (children.size() > 0) {
                m1.put("children", children);
            }
        }

    }

    @Transactional
    public void saveRole(RoleAddParam role) {
        SysRole r = new SysRole();
        BeanUtils.copyProperties(role, r);
        if (null == r.getId()) {
            this.commonService.commonAdd(r);
        } else {
            authService.deleteSysRoleAuthByRoleId(r.getId());
            this.commonService.commonSaveOrUpdate(r, r.getId());
        }
        //删除RoleAuth
        if (CollectionUtils.isNotEmpty(role.getCheckedKeys())) {
            authService.batchSaveRoleAuth(role.getCheckedKeys(), r, role.getPlat());
        }
    }


    @GuavaLocalCache(keyExt = "#uri+#roleIdList", expireTime = 60 * 10, refreshTime = 60 * 5)
    public boolean queryCheckAuth(String uri, List<Long> roleIdList) {
        if (CollectionUtils.isEmpty(roleIdList)) {
            return false;
        }
        Set<Long> authIds = authService.queryAuthIdList(roleIdList);
        List<SysAuth> auths = commonService.findByAttributeList(SysAuth.class, "id", authIds);
        for (SysAuth a : auths) {
            if (StringUtils.isNoneBlank(a.getPath()) && antPathMatcher.match(a.getPath(), uri)) {
                return true;
            }
        }
        return false;
    }
}
